/**
 * 
 */
package upe.dsc.algorithms.swarm.util;
import sun.reflect.ReflectionFactory.GetReflectionFactoryAction;
import features.*;

import adaboostcn.Imagem;

/**
 * @author ajccl/danvalho
 * 
 */
public class Functions {
	private static final double PI = Math.PI;

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double sphere(double[] x) {
		double sphere = 0.0;
		for (int i = 0; i < x.length; i++) {
			sphere += Math.pow(x[i], 2);
		}
		return sphere;
	}

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double schwefel12(double x[]) {
		double schwefel12 = 0.0;
		double tmp = 0.0;
		for (int i = 0; i < x.length; i++) {
			for (int j = 0; j <= i; j++) {
				tmp += x[j];
			}
			schwefel12 += Math.pow(tmp, 2);
		}
		return schwefel12;
	}

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double rosenbrock(double x[]) {
		double rosenbrock = 0.0;
		for (int i = 0; i < x.length - 1; i++) {
			rosenbrock += 100 * Math.pow(x[i + 1] - Math.pow(x[i], 2), 2)
					+ Math.pow(x[i] - 1, 2);
		}
		return rosenbrock;
	}

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double generalizedSchwefel26(double x[]) {
		double generalizedSchwefel26 = 0.0;
		for (int i = 0; i < x.length; i++) {
			generalizedSchwefel26 += x[i] * Math.sin(Math.sqrt(x[i]));
		}
		return generalizedSchwefel26 * (-1);
	}

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double generalizedRastrigin(double x[]) {
		double generalizedRastrigin = 0.0;
		for (int i = 0; i < x.length; i++) {
			generalizedRastrigin += Math.pow(x[i], 2) - 10.0
					* Math.cos(2.0 * PI * x[i]) + 10.0;
		}
		return generalizedRastrigin;
	}

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double ackley(double x[]) {
		double sum1 = 0.0;
		double sum2 = 0.0;
		for (int i = 0; i < x.length; i++) {
			sum1 += Math.pow(x[i], 2);
			sum2 += Math.cos(2 * PI * x[i]);
		}
		return (-20.0 * Math.exp((-0.2 * Math.sqrt((1.0 / x.length) * sum1)))
				- Math.exp((1.0 / x.length) * sum2) + 20.0 + Math.E);
	}

	/**
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double generalizedGriewank(double x[]) {
		double sum = 0.0;
		double prod = 1.0;

		sum += Math.pow(x[0], 2);
		prod *= Math.cos(x[0]);
		for (int i = 1; i < x.length; i++) {
			sum += Math.pow(x[i], 2);
			prod *= Math.cos(x[i] / Math.sqrt(i));
		}
		System.out.println("## SOMA: " + sum);
		System.out.println("## PROD: " + prod);
		return (sum / 4000.0) - prod + 1.0;
	}

	/**
	 * Uses mi function
	 * 
	 * @param x
	 *            n dimensions permitted
	 * @return
	 */
	public static double penalizedFuncionP8(double x[]) {
		double y[] = new double[x.length];
		double sum1 = 0.0;
		double sum2 = 0.0;
		double sum3 = 0.0;
		for (int i = 0; i < x.length; i++) {
			y[i] = 1.0 + (x[i] + 1) / 4;
			sum1 += 10.0 * Math.pow(Math.sin(PI * y[i]), 2);
			sum3 += mi(x[i], 10, 100, 4);
		}
		for (int i = 0; i < x.length - 1; i++) {
			sum2 += Math.pow(y[i] - 1, 2)
					* (1.0 + 10.0 * Math.pow(Math.sin(PI * y[i + 1]), 2));
		}
		return (PI * (sum1 + sum2 + Math.pow(y[x.length - 1] - 1, 2)) / x.length)
				+ sum3;
	}

	/**
	 * Uses mi function
	 * 
	 * @param x
	 * @return
	 */
	public static double penalizedFuncionP16(double x[]) {
		double sum1 = 0.0;
		double sum2 = 0.0;
		double sum3 = 0.0;
		for (int i = 0; i < x.length; i++) {
			sum1 += Math.pow(Math.sin(3.0 * PI * x[i]), 2);
			sum3 += mi(x[i], 5, 100, 4);
		}
		for (int i = 0; i < x.length - 1; i++) {
			sum2 += Math.pow(x[i] - 1, 2)
					* (1.0 + Math.pow(Math.sin(3.0 * PI * x[i + 1]), 2));
		}
		return 0.1
				* (sum1 + sum2 + Math.pow(x[x.length - 1] - 1, 2)
						* (1.0 + Math
								.pow(Math.sin(2 * PI * x[x.length - 1]), 2)))
				+ sum3;
	}

	/**
	 * Used by penalizedFuncionP8 and penalizedFuncionP16 function
	 * 
	 * @param x
	 * @param a
	 * @param k
	 * @param m
	 * @return
	 */
	private static double mi(double x, int a, int k, int m) {
		if (x > a) {
			return k * Math.pow(x - a, m);
		} else if (x < -a) {
			return k * Math.pow(-x - a, m);
		} else {
			// if ((x >= -a) && (x <= a))
			return 0;
		}
	}

	/**
	 * @param x
	 *            Only 2 dimensions permitted
	 * @return
	 */
	public static double sixhumpCamelback(double x[]) {
		return 4.0 * Math.pow(x[0], 2) - 2.1 * Math.pow(x[0], 4) + 1.0 / 3.0
				* Math.pow(x[0], 6) + x[0] * x[1] - 4.0 * Math.pow(x[1], 2)
				+ 4.0 * Math.pow(x[1], 4);
	}

	/**
	 * @param x
	 *            Only 2 dimensions permitted
	 * @return
	 */
	public static double goldensteinPrice(double x[]) {
		return (1 + Math.pow(x[0] + x[1] + 1, 2)
				* (19.0 - 14.0 * x[0] + 3.0 * Math.pow(x[0], 2) - 14.0 * x[1]
						+ 6.0 * x[0] * x[1] + 3.0 * Math.pow(x[1], 2)))
				* (30.0 + Math.pow(2.0 * x[0] - 3.0 * x[1], 2)
						* (18.0 - 32.0 * x[0] + 12.0 * Math.pow(x[0], 2) + 48.0
								* x[1] - 36.0 * x[0] * x[1] + 27.0 * Math.pow(
								x[1], 2)));
	}

	public static double adaboost(Imagem imagens[], int tipo, int _x, int _y,
			int _dx, int _dy,int nPositivas, int nNegativas) {

		double sumNegativas = 0;
		double sumPositivas = 0;
		Features feature = null;
		switch (tipo) {

			case 1: {
				feature = new Tipo1(_x, _y, _dx, _dy);
				break;
			}
			case 2: {
				feature = new Tipo2(_x, _y, _dx, _dy);
				break;
			}

			case 3: {
				feature = new Tipo3(_x, _y, _dx, _dy);
				break;
			}

			case 4: {
				feature = new Tipo4(_x, _y, _dx, _dy);
				break;
			}

			case 5: {
				feature = new Tipo5(_x, _y, _dx, _dy);
				break;
			}

			case 6: {
				feature = new Tipo6(_x, _y, _dx, _dy);
				break;
			}

			case 7: {
				feature = new Tipo7(_x, _y, _dx, _dy);
				break;
			}

			case 8: {
				feature = new Tipo8(_x, _y, _dx, _dy);
				break;
			}

			case 9: {
				feature = new Tipo9(_x, _y, _dx, _dy);
				break;
			}

			case 10: {
				feature = new Tipo10(_x, _y, _dx, _dy);
				break;
			}

			case 11: {
				feature = new Tipo11(_x, _y, _dx, _dy);
				break;
			}

			case 12: {
				feature = new Tipo12(_x, _y, _dx, _dy);
				break;
			}

			case 13: {
				feature = new Tipo13(_x, _y, _dx, _dy);
				break;
			}

			default:{
				break;
			}
		}
		
		double values[] = new double[imagens.length];
		double h[] = new double[imagens.length];
		
		for(int i =0;i<imagens.length;i++){			
			values[i] = feature.featureValue(imagens[i].getImagem());		
			if(imagens[i].getValor() == 0)
				sumNegativas+=values[i];
			else
				sumPositivas+=values[i];
		}
		feature.calculaLimiar(nPositivas, nNegativas, sumNegativas, sumPositivas);
		double erro = 0;
		for(int i = 0; i< imagens.length; i++){
			h[i] = feature.calculaH(values[i], 1);
			erro+= (h[i] - imagens[i].getValor());
		}
		erro = erro/imagens.length;
		

		return erro;
	}

}
